package com.sdap2.service.impl;

import com.sdap2.common.exception.BusinessException;
import com.sdap2.common.utils.WebClientUtil;
import com.sdap2.dao.DeviceDAO;
import com.sdap2.dao.SoilDataDAO;
import com.sdap2.dao.CameraDataDAO;
import com.sdap2.dto.DeviceDTO;
import com.sdap2.dto.DeviceDataResponse;
import com.sdap2.entity.Device;
import com.sdap2.entity.Greenhouse;
import com.sdap2.entity.SysUser;
import com.sdap2.entity.enums.DeviceType;
import com.sdap2.service.DeviceService;
import com.sdap2.service.GreenhouseService;
import com.sdap2.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceServiceImpl implements DeviceService {
    
    private final DeviceDAO deviceDAO;
    private final GreenhouseService greenhouseService;
    private final SysUserService sysUserService;
    private final WebClientUtil webClientUtil;
    private final SoilDataDAO soilDataDAO;
    private final CameraDataDAO cameraDataDAO;
    
    @Override
    @Transactional
    public Device create(DeviceDTO dto, String token) {
        // 去除序列号前后的空格
        dto.setSerialNumber(dto.getSerialNumber().trim());
        
        // 检查序列号是否已存在
        if (deviceDAO.existsBySerialNumber(dto.getSerialNumber())) {
            throw new BusinessException("设备序列号已存在");
        }
        
        // 如果是摄像头，验证序列号格式
        if (DeviceType.CAMERA.equals(dto.getDeviceType())) {
            if (!dto.getSerialNumber().matches("^[A-Z0-9]{9,32}$")) {
                throw new BusinessException("摄像头序列号格式不正确，应为9-32位大写字母和数字组合");
            }
            if (dto.getMonitorUrl() == null) {
                throw new BusinessException("摄像头设备必须指定监控地址");
            }
        }
        
        // 检查大棚是否存在并且属于当前用户
        Greenhouse greenhouse = greenhouseService.getById(dto.getGreenhouseId(), token);
        
        Device device = new Device();
        BeanUtils.copyProperties(dto, device);
        
        // 如果没有指定单位，使用设备类型的默认单位
        if (device.getUnit() == null) {
            device.setUnit(dto.getDeviceType().getDefaultUnit());
        }
        
        device.setGreenhouse(greenhouse);
        device.setStatus(0); // 默认离线
        
        return deviceDAO.save(device);
    }
    
    @Override
    @Transactional
    public Device update(Long id, DeviceDTO dto, String token) {
        Device device = getById(id, token);
        
        // 如果修改了序列号，需要检查是否与其他设备冲突
        if (!device.getSerialNumber().equals(dto.getSerialNumber())) {
            if (deviceDAO.existsBySerialNumberAndIdNot(dto.getSerialNumber(), id)) {
                throw new BusinessException("设备序列号已存在");
            }
        }
        
        // 如果修改了大棚，需要检查新大棚是否存在并且属于当前用户
        if (!device.getGreenhouse().getId().equals(dto.getGreenhouseId())) {
            Greenhouse greenhouse = greenhouseService.getById(dto.getGreenhouseId(), token);
            device.setGreenhouse(greenhouse);
        }
        
        BeanUtils.copyProperties(dto, device, "status");
        return deviceDAO.save(device);
    }
    
    @Override
    @Transactional
    public void delete(Long id, String token) {
        Device device = getById(id, token);
        
        // 先删除设备相关的数据
        switch (device.getDeviceType()) {
            case SOIL_MOISTURE:
            case SOIL_TEMPERATURE:
                // 删除土壤数据
                soilDataDAO.deleteByDeviceId(device.getId());
                break;
            case CAMERA:
                // 删除摄像头数据
                cameraDataDAO.deleteByDeviceId(device.getId());
                break;
        }
        
        // 最后删除设备
        deviceDAO.deleteById(id);
    }
    
    @Override
    public Device getById(Long id, String token) {
        SysUser currentUser = sysUserService.getCurrentUser(token);
        if (currentUser == null) {
            throw new BusinessException("用户未登录");
        }
        
        Device device = deviceDAO.findById(id)
                .orElseThrow(() -> new BusinessException("设备不存在"));
                
        // 检查设备是否属于当前用户
        if (!device.getGreenhouse().getUser().getId().equals(currentUser.getId())) {
            throw new BusinessException("无权操作此设备");
        }
        
        return device;
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<Device> getPage(Long greenhouseId, Pageable pageable, String token) {
        // 检查大棚是否存在并且属于当前用户
        greenhouseService.getById(greenhouseId, token);
        
        // 使用新的查询方法
        return deviceDAO.findAllByGreenhouseIdWithFetch(greenhouseId, pageable);
    }
    
    @Override
    public boolean checkDeviceOwnership(Long deviceId, String token) {
        try {
            SysUser currentUser = sysUserService.getCurrentUser(token);
            if (currentUser == null) {
                return false;
            }
            
            Device device = deviceDAO.findById(deviceId)
                    .orElseThrow(() -> new BusinessException("设备不存在"));
                    
            // 检查设备是否属于当前用户
            return device.getGreenhouse().getUser().getId().equals(currentUser.getId());
        } catch (Exception e) {
            return false;
        }
    }
    
    @Override
    public Page<?> getDeviceRecords(String serialNumber, LocalDateTime startTime,
                                 LocalDateTime endTime, Pageable pageable, String token) {
        Device device = deviceDAO.findBySerialNumber(serialNumber)
                .orElseThrow(() -> new BusinessException("设备不存在"));

        if (!checkDeviceOwnership(device.getId(), token)) {
            throw new BusinessException("无权操作此设备");
        }

        // 根据设备类型查询对应的数据表
        switch (device.getDeviceType()) {
            case SOIL_MOISTURE:
            case SOIL_TEMPERATURE:
                return soilDataDAO.findByDeviceIdAndCreateTimeBetween(
                    device.getId(), startTime, endTime, pageable);
            case CAMERA:
                return cameraDataDAO.findAllByDeviceIdAndCreateTimeBetween(
                    device.getId(), startTime, endTime, pageable);
            default:
                throw new BusinessException("不支持的设备类型");
        }
    }

    @Override
    public Object getLatestRecord(String serialNumber, String token) {
        Device device = deviceDAO.findBySerialNumber(serialNumber)
                .orElseThrow(() -> new BusinessException("设备不存在"));

        if (!checkDeviceOwnership(device.getId(), token)) {
            throw new BusinessException("无权操作此设备");
        }

        // 根据设备类型查询最新记录
        switch (device.getDeviceType()) {
            case SOIL_MOISTURE:
            case SOIL_TEMPERATURE:
                return soilDataDAO.findFirstByDeviceIdOrderByCreateTimeDesc(device.getId())
                    .orElseThrow(() -> new BusinessException("未找到设备记录"));
            case CAMERA:
                return cameraDataDAO.findFirstByDeviceIdOrderByCreateTimeDesc(device.getId())
                    .orElseThrow(() -> new BusinessException("未找到设备记录"));
            default:
                throw new BusinessException("不支持的设备类型");
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Optional<Device> getDeviceBySerialNumber(String serialNumber) {
        // 打印日志，查看实际的序列号
        log.info("Searching for device with serialNumber: {}", serialNumber);
        
        // 先尝试直接匹配
        Optional<Device> device = deviceDAO.findBySerialNumber(serialNumber);
        log.info("Direct match result: {}", device.isPresent() ? "found" : "not found");
        
        if (device.isPresent()) {
            return device;
        }
        
        // 如果找不到，尝试模糊匹配
        List<Device> devices = deviceDAO.findBySerialNumberContaining(serialNumber);
        log.info("Fuzzy match result count: {}", devices.size());
        if (!devices.isEmpty()) {
            log.info("Found device with serial number: {}", devices.get(0).getSerialNumber());
        }
        
        return devices.isEmpty() ? Optional.empty() : Optional.of(devices.get(0));
    }
} 